---@module tool
-- 工具方法

module(..., package.seeall)

--[[
print_dump是一个用于调试输出数据的函数，能够打印出nil,boolean,number,string,table类型的数据，以及table类型值的元表
参数data表示要输出的数据
参数showMetatable表示是否要输出元表
参数lastCount用于格式控制，用户请勿使用该变量
]]
function print_dump(data, showMetatable, lastCount)
    if type(data) ~= "table" then
        --Value
        if type(data) == "string" then
            io.write("\"", data, "\"")
        else
            io.write(tostring(data))
        end
    else
        --Format
        local count = lastCount or 0
        count = count + 1
        io.write("{\n")
        --Metatable
        if showMetatable then
            for i = 1,count do io.write("\t") end
            local mt = getmetatable(data)
            io.write("\"__metatable\" = ")
            print_dump(mt, showMetatable, count)    -- 如果不想看到元表的元表，可将showMetatable处填nil
            io.write(",\n")     --如果不想在元表后加逗号，可以删除这里的逗号
        end
        --Key
        for key,value in pairs(data) do
            for i = 1,count do io.write("\t") end
            if type(key) == "string" then
                io.write("\"", key, "\" = ")
            elseif type(key) == "number" then
                io.write("[", key, "] = ")
            else
                io.write(tostring(key))
            end
            print_dump(value, showMetatable, count) -- 如果不想看到子table的元表，可将showMetatable处填nil
            io.write(",\n")     --如果不想在table的每一个item后加逗号，可以删除这里的逗号
        end
        --Format
        for i = 1,lastCount or 0 do io.write("\t") end
        io.write("}")
    end
    --Format
    if not lastCount then
        io.write("\n")
    end
end


-- 表深复制 请使用pl-tablex.deepcopy
-- t1 = {a='a','s','d','f','g','h'}
-- t2 = DeepCopy(t1);  --深拷贝
-- t3 = t1; --浅拷贝
function deepCopy(obj)
    local InTable = {};
    local function Func(obj)
        if type(obj) ~= "table" then   --判断表中是否有表
            return obj;
        end
        local NewTable = {};  --定义一个新表
        InTable[obj] = NewTable;  --若表中有表，则先把表给InTable，再用NewTable去接收内嵌的表
        for k,v in pairs(obj) do  --把旧表的key和Value赋给新表
            NewTable[Func(k)] = Func(v);
        end
        return setmetatable(NewTable, getmetatable(obj))--赋值元表
    end
    return Func(obj) --若表中有表，则把内嵌的表也复制了
end

--判断表是否为空 a={}
function table_is_empty(t)
    if type(t) ~= "table" then
        return false
    end
    return _G.next( t ) == nil
end



-- 测试代码如下
-- data = {["a"] = "a", ["b"] = "b", [1] = 1, [2] = 2, ["t"] = {1, 2, 3}}
-- local sz = serialize(data)
-- print(sz)
-- print("---------")
-- print(serialize(unserialize(sz)))


--    table中删除指定元素(非根据索引)
--    @param    array 要操作的容器
--    @param     value 删除value值
--     @param    removeadll 是否删除所有相同的值
--    @return 返回删除值的个数
function removebyvalue(array, value, removeadll)
    --    deleteNum用于接收/返回删除个数; i/max 构成控制while循环
    local deleteNum,i,max=0,1,#array
    while i<=max do
        if array[i] == value then
            --    通过索引操作表的删除元素
            table.remove(array,i)
            --    标记删除次数
            deleteNum = deleteNum+1
            i = i-1
            --    控制while循环操作
            max = max-1
            --    判断是否删除所有相同的value值
            if not removeadll then break end
        end
        i= i+1
    end
    --    返回删除次数
    return deleteNum
end


-- 遍历数组 判断数组里是否有某个值
function isInTable(value, tbl)
    if tbl == nil then
        return false
    end
    for k,v in ipairs(tbl) do
        if v == value then
            return true;
        end
    end
    return false;
end

function confTableMerge(base, add,excludKey)
    for k,v in pairs(add) do
        if isInTable(k,excludKey) == true then

        else
            if type(v) == "table" then
                if type(base[k] or false) == "table" then
                    confTableMerge(base[k] or {}, add[k] or {},excludKey)
                else
                    -- 只有旧版本没有这个key才会合并过去
                    if base[k] == nil then
                        base[k] = v
                    end
                end
            else
                -- 只有旧版本没有这个key才会合并过去
                if base[k] == nil then
                    base[k] = v
                end
            end
        end
    end
    return base
end

-- 返回4个字符，从高到低
function uint32To4Bit(num)
    local b1 = bit.rshift(num,24)
    local b2 = bit.rshift(bit.band(num,0x00ff0000),16)
    local b3 = bit.rshift(bit.band(num,0x0000ff00),8)
    local b4 = bit.band(num,0x000000ff)
    return b1,b2,b3,b4
end

-- 返回4个字符，从高到低
function uint16To2Bit(num)
    return bit.rshift(num,8),bit.band(num,0x00ff)
end




-- 通过misc 模块获取时间戳
-- 因为4g模块不能通过os.time() 获取时间戳需要使用这个方式
function miscTime()
    -- local dt = {year=2013, month=12, day=25, hour=0, min=0, sec=0}
    -- print(os.time(dt))
    local tm = misc.getClock()
    return os.time(tm)
end


-- 获取当前时间 字符串
function getTimeStr()
    -- TODO 在中午12：58：00 时会转换为 00：58：00 不知道什么原因
    local tm = misc.getClock()
    local now = string.format("%04d-%02d-%02d %02d:%02d:%02d",tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec)
    return now
end

-- 获取当前时间 字符串
function getTimeStrDay()
    local tm = misc.getClock()
    local now = string.format("%04d-%02d-%02d",tm.year,tm.month,tm.day)
    return now
end
---\brief  将表示2个16进制数的字符串，转成1个16进制数
---\param  str     原始字符串
---\param  index1、index2    截取字符串2个字节的头尾位置
---\return  返回是否成功
---\return  返回一个16位数
function str16ToHex(str, index1, index2)
    local high, low = string.byte(str, index1, index2)
    if high and low then
        --high << 8 | low
        return true, bit.bor(bit.lshift(high, 8), low)
    else
        return false, 0x0000
    end
end


--[[ 判断一个table对象是否为 存放数组的类型还是存放table的类型
    print(checkTableItemIsArry({a={1,23},a1={1,23},a2={1,23}}))  false
    print(checkTableItemIsArry({1,2,3})) true
    print(checkTableItemIsArry({a=1,b=2,c=3})) false
    print(checkTableItemIsArry({a=1,b=2,c={1,2,3}})) false
--]]
function checkTableItemIsArry(tab)
    local re = true
    for k,v in pairs(tab) do
        --print(k)
        --print_dump(v)
        -- 所有元素都必须是 key 为number value不为table才返回ture
        if type(k) ~= "number" or type(v) == "table" then
            re = false
        end
    end
    return re
end


--[[ 对比两个数组 数组里面的元素非table 是否一样 比如{1,2,3}=={1,2,3} true
    print(isSameArry({1,2,3},{1,2,3})) true
    print(isSameArry({1,2},{1,2,3})) false
    print(isSameArry({},{1,2,3})) false
    print(isSameArry({1,2},{})) false
    print(isSameArry({},{})) true
--]]
function isSameArry(a1,a2)
    if #a2 ~= #a1 then
        return false
    end
    -- 以元素多的遍历
    local paisArr = a1
    local checkArr = a2
    if #a2 > #a1 then
        paisArr = a2
        checkArr = a1
    end
    for k,v in ipairs(paisArr) do
        if v ~=  checkArr[k] then
            return false
        end
    end
    return true
end



--[[ 对比两个table（key结构一样一般是修改前后不同的配置文件）
-- 返回对比后不同的内容（节约流量）
-- local t1 = {id=1,state=0,isUse={aa=2,bb={cc=78}},dtype="S_FAN_SUIDAO",list={{uu=1},{uu=2},{uu=3}}}
-- local t2 = {id=1,state=1,isUse={aa=3,bb={cc=71}},dtype="S_FAN_SUI22DAO",list={{uu=1},{uu=4},{uu=3}}}
-- print_dump(keySameDiff(t1,t2))
--    {
--        "dtype" = "S_FAN_SUI22DAO",
--         "state" = 1,
--          "isUse" = {
--              "aa" = 3,
--              "bb" = {
--                  "cc" = 71,
--              },
--           },
--          "list" = {
--              [1] = {},
--              [2] = {
--                  "uu" = 4,
--              },
--              [3] = {}
--          },
--    }
-- 如果是完全一样 返回 {}
]]--
function keySameDiff(old,new)
    --log.info("keySameDiff","--old")
    --print_dump(old)
    --log.info("keySameDiff","--new")
    --print_dump(new)
    local re = {}
    for k,v in pairs(new) do
        if type(v) == "table" then
            --  或者a = {}的类型
            --if tool.table_is_empty(v) == true then
            --    re[k] = {}
            if checkTableItemIsArry(v) == true then
                --如果这个table是类似 a={1,3,4},
                -- 判断两个array是否完全一样
                if isSameArry(v,old[k]) == true then
                    -- 完全一样不作为差异
                else
                    re[k] = v -- 如果不一样完全返回最新
                end
            else
                local re1 = keySameDiff(old[k],new[k])
                if table_is_empty(re1) == false then
                    re[k] = re1
                else
                    if type(k) == "number" then
                        -- 数组里面的key，设置一个空table占位
                        re[k] = {}
                    end
                end
            end

        else
            if old[k] ~= new[k] then
                re[k] = new[k]
            end
        end
    end
    -- 判断返回的数组对象是否全部为空不要返回 {{}，{}，{}}类似的table
    local is_array_same = true
    for k1,v1 in pairs(re) do
        -- 有一个key不是数字代表他不是数组
        if type(k1) ~= "number" then
            -- 不是一个array
            is_array_same = false
            break
        end

        if table_is_empty(v1) == false then
            -- 有不一样的地方
            is_array_same = false
            break
        end

    end
    if is_array_same == true then
        re = {} -- 重新变为{} 不是{{}，{}，{}}类似的table
    end
    return re
end

function tableMerge(src,tar)
    for k,v in pairs(src) do
        if type(v) == "table" then
            if table_is_empty(v) == true and type(k) == "number" then
                -- 数组不变导致的{}
            elseif checkTableItemIsArry(v) == true then
                --如果这个table是类似 a={1,3,4},
                tar[k] = v -- 如果不一样完全返回最新
            else
                tableMerge(src[k],tar[k])
            end
        else
            -- 数值
            tar[k] = v
        end
    end
end
--[[
 传入一个配置文件的diff合并到主配置文件中
 diffstr == "gearLevel":[{},{"totalVen":0,"openDev":[]},{},{},{},{},{},{},{},{},{},{},{},{},{}]
 conf 目前为smartconfig 或 deviceconfig
 -- 返回 是否成功
]]
function mergeByDiff(diffstr,conf)
    log.info("tool-mergeByDiff==")
    print_dump(diffstr)
    tableMerge(diffstr,conf)
    return true
end

-- 一次合并多个简单表
function mergeTables(...)
    local tabs = {...}
    if not tabs then
        return {}
    end
    local origin = tabs[1]
    for i = 2,#tabs do
        if origin then
            if tabs[i] then
                for k,v in pairs(tabs[i]) do
                    table.insert(origin,v)
                end
            end
        else
            origin = tabs[i]
        end
    end
    return origin
end

--将一个2字节长度的数组转换成为一个1字节的数组
--比如{0x1234，0x2234, 0x3234} -> {0x12,0x34,0x22,0x34,0x32,0x34}
function intTable2charTable(src)
    local re = {}
    for key,value in ipairs(src) do
        table.insert(re, bit.band(bit.rshift(value, 8), 0xff))
        table.insert(re, bit.band(value, 0xff))
    end
    return re
end

--查找指定数值在table里面的位置
function findIndex(T, value)
    for k,v in ipairs(T) do
        if value == v then
            return k
        end
    end
    return -1
end

-- 字符串分割
function strSplit(str,delimiter)
    local dLen = string.len(delimiter)
    local newDeli = ''
    for i=1,dLen,1 do
        newDeli = newDeli .. "["..string.sub(delimiter,i,i).."]"
    end

    local locaStart,locaEnd = string.find(str,newDeli)
    local arr = {}
    local n = 1
    while locaStart ~= nil
    do
        if locaStart>0 then
            arr[n] = string.sub(str,1,locaStart-1)
            n = n + 1
        end

        str = string.sub(str,locaEnd+1,string.len(str))
        locaStart,locaEnd = string.find(str,newDeli)
    end
    if str ~= nil then
        arr[n] = str
    end
    return arr,n
end

-- 字符串转浮点数 IEEE754
-- hexToFloat("406449BA") = 3.566999912262
function hexToFloat( hexString )
    if hexString == nil then
        return 0
    end
    local t = type( hexString )
    if t == "string" then
        hexString = tonumber(hexString , 16)
    end

    local hexNums = hexString

    local sign = math.modf(hexNums/(2^31))

    local exponent = hexNums % (2^31)
    exponent = math.modf(exponent/(2^23)) -127

    local mantissa = hexNums % (2^23)

    for i=1,23 do
        mantissa = mantissa / 2
    end
    mantissa = 1+mantissa
    --	print(mantissa)
    local result = (-1)^sign * mantissa * 2^exponent
    return result
end

-- hexToFloat1(string.char(0x3d,0x75,0xc2,0x8f)) = 0.06
function hexToFloat1( hexString )
    --local nextpox3,number1=pack.unpack(string.char(0x3d,0x75,0xc2,0x8f),">f")--输出为100，因为短型是2个字节  0.06 测试地址http://www.styb.cn/cms/ieee_754.php
    local nextpox3,number1=pack.unpack(hexString,">f")
    return number1
end


-- 浮点数转字符串 IEEE754
function FloatToHex( floatNum )
    local S = 0
    local E = 0
    local M = 0
    if floatNum == 0 then
        return "00000000"
    end
    local num1,num2 = math.modf(floatNum/1)
    local InterPart = num1

    if floatNum > 0 then
        S = 0 * 2^31
    else
        S = 1 * 2^31
    end
    local intercount = 0
    repeat
        num1 = math.modf(num1/2)
        intercount = intercount + 1
    until (num1 == 0)

    E = intercount - 1
    local Elen = 23 - E
    InterPart = InterPart % (2^E)
    InterPart = InterPart * (2^Elen)

    E = E + 127
    E = E * 2^23

    for i=1,Elen do
        num2 = num2 * 2
        num1,num2 = math.modf(num2/1)
        M = M + num1 * 2^(Elen - i)
    end

    M = InterPart + M

    --E值为整数部分转成二进制数后左移位数：22.8125 转成二进制10110.1101，左移4位 1.01101101
    --E=4 ，再加上127 就为所需E值
    --010000011 01101101 000000000000000

    local Result = S + E + M

    Result = string.format("%08X",Result)
    return Result
end

-- 分割字符串
function Split(szFullString, szSeparator)
    local nFindStartIndex = 1
    local nSplitIndex = 1
    local nSplitArray = {}
    while true do
        local nFindLastIndex = string.find(szFullString, szSeparator,
                                           nFindStartIndex)
        if not nFindLastIndex then
            nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex,
                                                  string.len(szFullString))
            break
        end
        nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex,
                                              nFindLastIndex - 1)
        nFindStartIndex = nFindLastIndex + string.len(szSeparator)
        nSplitIndex = nSplitIndex + 1
    end
    return nSplitArray
end

